
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace PolicyAlgebra.Util
{
	///<summary>
	///    This class defines a generic set (i.e. a set that will
	///    contain sets of a paramerical type).
	///</summary>
	///<remarks>
	///    The class currently uses a list to store the set's members.
	///    Future versions will use trees or similar data structures 
	///    to scale with the size of the set.
	///</remarks>
	public class Set<T> : IEnumerable, IEnumerator
	{ 
		private IEnumerator enumerator; // Used in the IEnumerator methods
		private LinkedList<T> set;

		///<summary>
		/// The first constructor for this class. It takes a varying number 
		/// of parameters and adds all of them as members of the new set.
		///</summary>
		///<param name="members">The list of parameters that is passed to
		/// the constructor. All of them are added to the new set.
		///</param>
		public Set(params T[] members)
		{
			enumerator = null;
			set = new LinkedList<T>();

			foreach (T m in members) {
				if (!set.Contains(m)) {
					set.AddLast(m);
				}
			}
		}


		///<summary>
		/// The second constructor for this class.
		/// parameters and adds all of them as members of the new set.
		///</summary>
		///<param name="s">A set that will be used to fill this set with members.
		///</param>
		public Set(Set<T> s)
		{
			this.set = s.set;
		}


		///<summary>
		/// This method adds a new member to the set.
		///</summary>
		///<param bame="m">The new member to add to the set.</param>
		public void AddMember(T m)
		{
			if (!set.Contains(m)) {
				set.AddLast(m); 
			}
		}
		

		///<summary>
		/// This method adds the members from another set to this set.
		///</summary>
		///<param bame="s">The set to add members from.</param>
		public void AddMembers(Set<T> s)
		{
			foreach (T m in s) {
				AddMember(m);
			}
		}


		///<summary>
		/// Checks for membership in the set.
		///</summary>
		///<param name="m">The potential member</param>
		///<returns>True if m is a member, else False</returns>
		public bool IsMember(T m)
		{
			return set.Contains(m);
		}

		///<summary>
		/// Translates this set into a string for nice printing
		///</summary>
		public override string ToString()
		{
			StringBuilder sb = new StringBuilder("{ ");

			foreach (T m in set) {
				sb.Append(m.ToString());
				sb.Append(" ");
			}

			sb.Append("}");

			return sb.ToString();
		}


		// The standard set operations of "conjunction," "disjunction"
		// and difference follows. 

		///<summary>
		/// Takes the conjunction of two sets, leaving both of the original
		/// sets intact.
		///</summary>
		///<param name="s1">The first set</param>
		///<param name="s2">The second set</param>
		///<returns>
		/// A new set containing the conjunction of the two supplied sets
		///</returns>
		public static Set<T> Conjunction(Set<T> s1, Set<T> s2)
		{
			Set<T> newSet = new Set<T>();

			foreach (T m in s1)
			{
				if (s2.IsMember(m)) {
					newSet.AddMember(m);
				}
			}

			return newSet;
		}


		///<summary>
		/// Cpmputes the conjunction of two sets, leaving both of the original
		/// sets intact.
		///</summary>
		///<param name="s1">The first set</param>
		///<param name="s2">The second set</param>
		///<returns>
		/// A new set containing the disjunction of the two supplied sets
		///</returns>
		public static Set<T> Disjunction(Set<T> s1, Set<T> s2)
		{
			Set<T> newSet = new Set<T>();

			foreach (T m in s1)
			{
				newSet.AddMember(m);
			}

			foreach (T m in s2)
			{
				newSet.AddMember(m);
			}


			return newSet;
		}


		///<summary>
		/// Computes a hash code for this set.
		///</summary>
		///<returns>
		/// The integer hash value
		///</returns>
		public override int GetHashCode()
		{
			return set.GetHashCode();
		}


		// IEnumerable and IEnumerator implementation follows,
		// the current implementatin simply wrapps it around the
		// methods provided by the LinkedList class.
		public IEnumerator GetEnumerator()
		{
			enumerator = set.GetEnumerator();
			return enumerator;
		}

		public void Reset()
		{
			enumerator.Reset();
		}

		public bool MoveNext()
		{
			return enumerator.MoveNext();
		}

		public object Current 
		{
			get 
			{
				return enumerator.Current;
			}
		}
	}
}
